Importing Libraries
from PIL import Image
import numpy as np
import os
from matplotlib.pyplot import imshow
import matplotlib.pyplot as plt
import cv2
import random
from math import log
from google.colab.patches import cv2_imshow
from tqdm import tqdm
Downloading sample images
!wget https://drive.google.com/uc?id=1Djfm4PqE7Su4WqEdZKiGL-8HtrbVBuMm
!mv uc?id=1Djfm4PqE7Su4WqEdZKiGL-8HtrbVBuMm HorizonZero.png
!wget https://drive.google.com/uc?id=19xZhsjs_r0tLwtu_Wl5DB5rG26dhw069
!mv uc?id=19xZhsjs_r0tLwtu_Wl5DB5rG26dhw069 lena.bmp
--2025-07-05 08:49:41-- https://drive.google.com/uc?id=1Djfm4PqE7Su4WqEdZKiGL-8HtrbVBuMm Resolving drive.google.com (drive.google.com)... 142.251.163.113, 142.251.163.101, 142.251.163.139, ... Connecting to drive.google.com (drive.google.com)|142.251.163.113|:443... connected. HTTP request sent, awaiting response... 303 See Other Location: https://drive.usercontent.google.com/download?id=1Djfm4PqE7Su4WqEdZKiGL-8HtrbVBuMm [following] --2025-07-05 08:49:41-- https://drive.usercontent.google.com/download?id=1Djfm4PqE7Su4WqEdZKiGL-8HtrbVBuMm Resolving drive.usercontent.google.com (drive.usercontent.google.com)... 142.251.167.132, 2607:f8b0:4004:c1d::84 Connecting to drive.usercontent.google.com (drive.usercontent.google.com)|142.251.167.132|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 111636 (109K) [image/png] Saving to: ‘uc?id=1Djfm4PqE7Su4WqEdZKiGL-8HtrbVBuMm’ uc?id=1Djfm4PqE7Su4 100%[===================>] 109.02K --.-KB/s in 0.01s 2025-07-05 08:49:43 (10.4 MB/s) - ‘uc?id=1Djfm4PqE7Su4WqEdZKiGL-8HtrbVBuMm’ saved [111636/111636] --2025-07-05 08:49:43-- https://drive.google.com/uc?id=19xZhsjs_r0tLwtu_Wl5DB5rG26dhw069 Resolving drive.google.com (drive.google.com)... 142.251.163.113, 142.251.163.101, 142.251.163.139, ... Connecting to drive.google.com (drive.google.com)|142.251.163.113|:443... connected. HTTP request sent, awaiting response... 303 See Other Location: https://drive.usercontent.google.com/download?id=19xZhsjs_r0tLwtu_Wl5DB5rG26dhw069 [following] --2025-07-05 08:49:43-- https://drive.usercontent.google.com/download?id=19xZhsjs_r0tLwtu_Wl5DB5rG26dhw069 Resolving drive.usercontent.google.com (drive.usercontent.google.com)... 142.251.167.132, 2607:f8b0:4004:c1d::84 Connecting to drive.usercontent.google.com (drive.usercontent.google.com)|142.251.167.132|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 263222 (257K) [application/octet-stream] Saving to: ‘uc?id=19xZhsjs_r0tLwtu_Wl5DB5rG26dhw069’ uc?id=19xZhsjs_r0tL 100%[===================>] 257.05K --.-KB/s in 0.03s 2025-07-05 08:49:44 (8.57 MB/s) - ‘uc?id=19xZhsjs_r0tLwtu_Wl5DB5rG26dhw069’ saved [263222/263222]
Image matrix from pixel access object
def getImageMatrix(imageName):
im = Image.open(imageName)
pix = im.load()
color = 1
if type(pix[0,0]) == int:
color = 0
image_size = im.size
image_matrix = []
for width in range(int(image_size[0])):
row = []
for height in range(int(image_size[1])):
row.append((pix[width,height]))
image_matrix.append(row)
return image_matrix, image_size[0], image_size[1],color
def getImageMatrix_gray(imageName):
im = Image.open(imageName).convert('LA')
pix = im.load()
image_size = im.size
image_matrix = []
for width in range(int(image_size[0])):
row = []
for height in range(int(image_size[1])):
row.append((pix[width,height]))
image_matrix.append(row)
return image_matrix, image_size[0], image_size[1],color
Arnold Cat Map
Arnold's cat map is a chaotic map often used for pixel manipulation. It applies a transform on the image that essentially shuffles the pixels by stretching anf folding thethe image. When an optimal number of iterations of the transformation is applied on the image, the resulting image becomes incomprehensible and hence encrypted. When the transformation is repeated enough times, the original image will reappear.
The transform applied on the image is: R([x,y]) = [(x + y) mod n, (x + 2y) mod n] where n is the dimensions of the image
def ArnoldCatTransform(img, num):
rows, cols, ch = img.shape
n = rows
img_arnold = np.zeros([rows, cols, ch])
for x in range(0, rows):
for y in range(0, cols):
img_arnold[x][y] = img[(x+y)%n][(x+2*y)%n]
return img_arnold
Arnold encryption
from tqdm import tqdm
def ArnoldCatEncryption(imageName, key):
img = cv2.imread(imageName)
for i in tqdm(range(0, key), desc="Encrypting"):
img = ArnoldCatTransform(img, i)
cv2.imwrite(imageName.split('.')[0] + "_ArnoldcatEnc.png", img)
return img
Arnold Decryption
def ArnoldCatDecryption(imageName, key):
img = cv2.imread(imageName)
rows, cols, ch = img.shape
dimension = rows
decrypt_it = dimension
if (dimension%2==0) and 5**int(round(log(dimension/2,5))) == int(dimension/2):
decrypt_it = 3*dimension
elif 5**int(round(log(dimension,5))) == int(dimension):
decrypt_it = 2*dimension
elif (dimension%6==0) and 5**int(round(log(dimension/6,5))) == int(dimension/6):
decrypt_it = 2*dimension
else:
decrypt_it = int(12*dimension/7)
for i in range(key,decrypt_it):
img = ArnoldCatTransform(img, i)
cv2.imwrite(imageName.split('_')[0] + "_ArnoldcatDec.png",img)
return img
image = "HorizonZero"
ext = ".png"
key = 20
img = cv2.imread(image + ext)
cv2_imshow(img)
ArnoldCatEncryptionIm = ArnoldCatEncryption(image + ext, key)
cv2_imshow(ArnoldCatEncryptionIm)
Encrypting: 100%|██████████| 20/20 [00:01<00:00, 16.84it/s]
ArnoldCatDecryptionIm = ArnoldCatDecryption(image + "_ArnoldcatEnc.png", key)
cv2_imshow(ArnoldCatDecryptionIm)
Histogram Analysis
The ciphertext image histogram analysis is one of the most straight-forward methods ofillustrating the image encryption quality. A good image encryption method tends to encrypt a plaintext image to a random incomprehensible form. Thus a good image encyption technique generates a cipher image that has a uniformly distributed intensity histogram.
image = "HorizonZero"
ext = ".png"
img = cv2.imread(image + ext,1)
pil_im = Image.open(image + ext, 'r')
imshow(np.asarray(pil_im))
plt.figure(figsize=(14,6))
histogram_blue = cv2.calcHist([img],[0],None,[256],[0,256])
plt.plot(histogram_blue, color='blue')
histogram_green = cv2.calcHist([img],[1],None,[256],[0,256])
plt.plot(histogram_green, color='green')
histogram_red = cv2.calcHist([img],[2],None,[256],[0,256])
plt.plot(histogram_red, color='red')
plt.title('Intensity Histogram - Original Image', fontsize=20)
plt.xlabel('pixel values', fontsize=16)
plt.ylabel('pixel count', fontsize=16)
plt.show()
image = "HorizonZero_ArnoldcatEnc"
ext = ".png"
img = cv2.imread(image + ext,1)
pil_im = Image.open(image + ext, 'r')
imshow(np.asarray(pil_im))
plt.figure(figsize=(14,6))
histogram_blue = cv2.calcHist([img],[0],None,[256],[0,256])
plt.plot(histogram_blue, color='blue')
histogram_green = cv2.calcHist([img],[1],None,[256],[0,256])
plt.plot(histogram_green, color='green')
histogram_red = cv2.calcHist([img],[2],None,[256],[0,256])
plt.plot(histogram_red, color='red')
plt.title('Intensity Histogram - Arnold Cat Encrypted', fontsize=20)
plt.xlabel('pixel values', fontsize=16)
plt.ylabel('pixel count', fontsize=16)
plt.show()
Adjacent Pixel Auto-Correlation
Since images exhibit high information redundancy, it is desirable to have an encryption algorithm that breaks this redundancy. Thus as a metric of encryption performance we find the correlation between adjacent pixels in a direction (Horizontal, Vertical or Diagonal). We have considered the Horizontal direction.
1024 random pixels are picked up from the image and its correlation between it's rightmost neighbour is found and plotted. For a good algorithm, the correlation plot should appear random with no discernable pattern
image = "HorizonZero"
ext = ".png"
img = Image.open(image+ext).convert('LA')
pil_im = Image.open(image + ext, 'r')
imshow(np.asarray(pil_im))
<matplotlib.image.AxesImage at 0x7e2e60761710>
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
import random
def getImageMatrix_gray(image_path):
img = Image.open(image_path).convert('L')
img_array = np.array(img)
image_size = img_array.shape[0]
return img_array, image_size
image = "HorizonZero"
ext = ".png"
ImageMatrix, image_size = getImageMatrix_gray(image + ext)
samples_x = []
samples_y = []
for i in range(1024):
x = random.randint(0, image_size - 2)
y = random.randint(0, image_size - 1)
samples_x.append(ImageMatrix[x][y])
samples_y.append(ImageMatrix[x + 1][y])
plt.figure(figsize=(10, 8))
plt.scatter(samples_x, samples_y, s=2)
plt.title('Adjacent Pixel Autocorrelation - Original Image', fontsize=20)
plt.show()
image = "HorizonZero_ArnoldcatEnc"
ext = ".png"
ImageMatrix,image_size = getImageMatrix_gray(image+ext)
samples_x = []
samples_y = []
print(image_size)
for i in range(1024):
x = random.randint(0,image_size-2)
y = random.randint(0,image_size-1)
samples_x.append(ImageMatrix[x][y])
samples_y.append(ImageMatrix[x+1][y])
plt.figure(figsize=(10,8))
plt.scatter(samples_x,samples_y,s=2)
plt.title('Adjacent Pixel Autocorrelation - Arnold Cat Encryption on Image', fontsize=20)
plt.show()
250
Henon Map
Henon chaotic map is a two-dimensional iterated discrete dynamic system that shows. chaotic character on specific values of the constants used. Classical Henon map have values of a = 1.4 and b = 0.3. For the classical values the Henon map is chaotic. For other values of a and b the map may be chaotic, intermittent, or converge to a periodic orbit.
Given initial conditions (x0,y0), a henon map is given by the following equations:
(Xn+1) = (Yn) + 1 − a.(Xn)
(Yn+1) = b * (Xn)
def dec(bitSequence):
decimal = 0
for bit in bitSequence:
decimal = decimal * 2 + int(bit)
return decimal
def genHenonMap(dimensionX, dimensionY, key):
x = key[0]
y = key[1]
sequenceSize = dimensionX * dimensionY * 8 #Total Number of bitSequence produced
bitSequence = [] #Each bitSequence contains 8 bits
byteArray = [] #Each byteArray contains m( i.e 512 in this case) bitSequence
TImageMatrix = [] #Each TImageMatrix contains m*n byteArray( i.e 512 byteArray in this case)
for i in range(sequenceSize):
xN = y + 1 - 1.4 * x**2
yN = 0.3 * x
x = xN
y = yN
if xN <= 0.4:
bit = 0
else:
bit = 1
try:
bitSequence.append(bit)
except:
bitSequence = [bit]
if i % 8 == 7:
decimal = dec(bitSequence)
try:
byteArray.append(decimal)
except:
byteArray = [decimal]
bitSequence = []
byteArraySize = dimensionX*8
if i % byteArraySize == byteArraySize-1:
try:
TImageMatrix.append(byteArray)
except:
TImageMatrix = [byteArray]
byteArray = []
return TImageMatrix
Henon Encryption
def HenonEncryption(imageName,key):
imageMatrix, dimensionX, dimensionY, color = getImageMatrix(imageName)
transformationMatrix = genHenonMap(dimensionX, dimensionY, key)
resultantMatrix = []
for i in range(dimensionX):
row = []
for j in range(dimensionY):
try:
if color:
row.append(tuple([transformationMatrix[i][j] ^ x for x in imageMatrix[i][j]]))
else:
row.append(transformationMatrix[i][j] ^ imageMatrix[i][j])
except:
if color:
row = [tuple([transformationMatrix[i][j] ^ x for x in imageMatrix[i][j]])]
else :
row = [transformationMatrix[i][j] ^ x for x in imageMatrix[i][j]]
try:
resultantMatrix.append(row)
except:
resultantMatrix = [row]
if color:
im = Image.new("RGB", (dimensionX, dimensionY))
else:
im = Image.new("L", (dimensionX, dimensionY)) # L is for Black and white pixels
pix = im.load()
for x in range(dimensionX):
for y in range(dimensionY):
pix[x, y] = resultantMatrix[x][y]
im.save(imageName.split('.')[0] + "_HenonEnc.png", "PNG")
Henon Decryption
def HenonDecryption(imageNameEnc, key):
imageMatrix, dimensionX, dimensionY, color = getImageMatrix(imageNameEnc)
transformationMatrix = genHenonMap(dimensionX, dimensionY, key)
henonDecryptedImage = []
for i in range(dimensionX):
row = []
for j in range(dimensionY):
try:
if color:
row.append(tuple([transformationMatrix[i][j] ^ x for x in imageMatrix[i][j]]))
else:
row.append(transformationMatrix[i][j] ^ imageMatrix[i][j])
except:
if color:
row = [tuple([transformationMatrix[i][j] ^ x for x in imageMatrix[i][j]])]
else :
row = [transformationMatrix[i][j] ^ x for x in imageMatrix[i][j]]
henonDecryptedImage.append(row)
if color:
im = Image.new("RGB", (dimensionX, dimensionY))
else:
im = Image.new("L", (dimensionX, dimensionY)) # L is for Black and white pixels
pix = im.load()
for x in range(dimensionX):
for y in range(dimensionY):
pix[x, y] = henonDecryptedImage[x][y]
im.save(imageNameEnc.split('_')[0] + "_HenonDec.png", "PNG")
image = "HorizonZero"
ext = ".png"
key = (0.1,0.1)
pip install Pillow
Requirement already satisfied: Pillow in /usr/local/lib/python3.11/dist-packages (11.2.1)
image = "HorizonZero"
ext = ".png"
img = cv2.imread(image + ext,1)
pil_im = Image.open(image + ext, 'r')
imshow(np.asarray(pil_im))
pil_im = Image.open(image + ext, 'r')
imshow(np.asarray(pil_im))
<matplotlib.image.AxesImage at 0x7e2e6079ed10>
image = "HorizonZero"
ext = ".png"
HenonEncryption(image + ext, key)
im = Image.open(image + "_HenonEnc.png", 'r')
imshow(np.asarray(im))
<matplotlib.image.AxesImage at 0x7e2e86717e10>
HenonDecryption(image + "_HenonEnc.png", key)
im = Image.open(image + "_HenonDec.png", 'r')
imshow(np.asarray(im))
<matplotlib.image.AxesImage at 0x7e2e682bd750>
Histogram analysis¶
#Original Image
image = "HorizonZero"
ext = ".png"
img = cv2.imread(image + ext,1)
histogram_blue = cv2.calcHist([img],[0],None,[256],[0,256])
plt.plot(histogram_blue, color='blue')
histogram_green = cv2.calcHist([img],[1],None,[256],[0,256])
plt.plot(histogram_green, color='green')
histogram_red = cv2.calcHist([img],[2],None,[256],[0,256])
plt.plot(histogram_red, color='red')
plt.title('Intensity Histogram - Original Image', fontsize=20)
plt.xlabel('pixel values', fontsize=16)
plt.ylabel('pixel count', fontsize=16)
plt.show()
#Encrypted Image
image = "HorizonZero_HenonEnc"
ext = ".png"
img = cv2.imread(image + ext,1)
histogram_blue = cv2.calcHist([img],[0],None,[256],[0,256])
plt.plot(histogram_blue, color='blue')
histogram_green = cv2.calcHist([img],[1],None,[256],[0,256])
plt.plot(histogram_green, color='green')
histogram_red = cv2.calcHist([img],[2],None,[256],[0,256])
plt.plot(histogram_red, color='red')
plt.title('Intensity Histogram - Henon Map Encrypted Image', fontsize=20)
plt.xlabel('pixel values', fontsize=16)
plt.ylabel('pixel count', fontsize=16)
plt.show()
Adjacent Pixel AutoCorrelation¶
#original image
image = "HorizonZero"
ext = ".png"
ImageMatrix,image_size = getImageMatrix_gray(image+ext)
samples_x = []
samples_y = []
for i in range(1024):
x = random.randint(0,image_size-2)
y = random.randint(0,image_size-1)
samples_x.append(ImageMatrix[x][y])
samples_y.append(ImageMatrix[x+1][y])
plt.figure(figsize=(10,8))
plt.scatter(samples_x,samples_y,s=2)
plt.title('Adjacent Pixel Autocorrelation - Original Image', fontsize=20)
plt.show()
#encrypted image
image = "HorizonZero_HenonEnc"
ext = ".png"
ImageMatrix,image_size = getImageMatrix_gray(image+ext)
samples_x = []
samples_y = []
print(image_size)
for i in range(1024):
x = random.randint(0,image_size-2)
y = random.randint(0,image_size-1)
samples_x.append(ImageMatrix[x][y])
samples_y.append(ImageMatrix[x+1][y])
plt.figure(figsize=(10,8))
plt.scatter(samples_x,samples_y,s=2)
plt.title('Adjacent Pixel Autocorrelation - Henon Encryption on Image', fontsize=20)
plt.show()
250
Key Sensitivity¶
image = "HorizonZero"
ext = ".png"
img = cv2.imread(image + ext,1)
pil_im = Image.open(image + ext, 'r')
imshow(np.asarray(pil_im))
<matplotlib.image.AxesImage at 0x7e2e48f06310>
ARNOLD CAT
#encypting with key=20
ArnoldCatEncryptionIm = ArnoldCatEncryption(image + ext, 20)
cv2_imshow(ArnoldCatEncryptionIm)
Encrypting: 100%|██████████| 20/20 [00:01<00:00, 16.95it/s]
#decrypting with key=19
ArnoldCatDecryptionIm = ArnoldCatDecryption(image + "_ArnoldcatEnc.png", 19)
cv2_imshow(ArnoldCatDecryptionIm)
HENON MAPS
#Encrypt with key (0.1, 0.1)
HenonEncryption(image + ext, (0.1, 0.1))
im = Image.open(image + "_HenonEnc.png", 'r')
imshow(np.asarray(im))
<matplotlib.image.AxesImage at 0x7e2e6065ce50>
#Decrypt with the key (0.1, 0.101)
HenonDecryption(image + "_HenonEnc.png", (0.1, 0.101))
im = Image.open(image + "_HenonDec.png", 'r')
imshow(np.asarray(im))
<matplotlib.image.AxesImage at 0x7e2e60309610>